    /* keywords are case insensitive */
%option caseless
    /* we don't need yywrap() function */
%option noyywrap
    /* we don't need yyunput() function */
%option nounput
    /* we don't need input() function */
%option noinput
    /* enable location */
%option bison-bridge
%option bison-locations

%{
#include "ast.h"
#include "yacc.tab.h"
#include "errors.h"
#include <iostream>

// automatically update location
#define YY_USER_ACTION \
    yylloc->first_line = yylloc->last_line; \
    yylloc->first_column = yylloc->last_column; \
    for (int i = 0; yytext[i] != '\0'; i++) { \
        if(yytext[i] == '\n') { \
            yylloc->last_line++; \
            yylloc->last_column = 1; \
        } else { \
            yylloc->last_column++; \
        } \
    }
bool check_time(std::string &datetime);
%}

alpha [a-zA-Z]
digit [0-9]
white_space [ \t]+
new_line "\r"|"\n"|"\r\n"
sign "+"|"-"
identifier {alpha}(_|{alpha}|{digit})*
value_int {sign}?{digit}+
value_float {sign}?{digit}+\.({digit}+)?
value_datetime '{digit}{1,4}-{digit}{1,2}-{digit}{1,2}\ {digit}{1,2}:{digit}{1,2}:{digit}{1,2}'
value_string '[^']*'
single_op ";"|"("|")"|","|"*"|"="|">"|"<"|"."

%x STATE_COMMENT

%%
    /* block comment */
"/*" { BEGIN(STATE_COMMENT); }
<STATE_COMMENT>"*/" { BEGIN(INITIAL); }
<STATE_COMMENT>[^*] { /* ignore the text of the comment */ }
<STATE_COMMENT>\* { /* ignore *'s that aren't part of */ }
    /* single line comment */
"--".* { /* ignore single line comment */ }
    /* white space and new line */
{white_space} { /* ignore white space */ }
{new_line} { /* ignore new line */ }
    /* keywords */
"SHOW" { return SHOW; }
"BEGIN" { return TXN_BEGIN; }
"COMMIT" { return TXN_COMMIT; }
"ABORT" { return TXN_ABORT; }
"ROLLBACK" { return TXN_ROLLBACK; }
"TABLES" { return TABLES; }
"CREATE" { return CREATE; }
"TABLE" { return TABLE; }
"DROP" { return DROP; }
"DESC" { return DESC; }
"INSERT" { return INSERT; }
"INTO" { return INTO; }
"VALUES" { return VALUES; }
"DELETE" { return DELETE; }
"FROM" { return FROM; }
"WHERE" { return WHERE; }
"UPDATE" { return UPDATE; }
"SET" { return SET; }
"SELECT" { return SELECT; }
"INT" { return INT; }
"CHAR" { return CHAR; }
"FLOAT" { return FLOAT; }
"BIGINT" {return BIGINT;}
"DATETIME" {return DATETIME;}
"INDEX" { return INDEX; }
"AND" { return AND; }
"JOIN" {return JOIN;}
"EXIT" { return EXIT; }
"HELP" { return HELP; }
"ORDER" { return ORDER; }
"BY" {  return BY;  }
"ASC" { return ASC; }
"LIMIT" {return LIMIT;}
    /* operators */
">=" { return GEQ; }
"<=" { return LEQ; }
"<>" { return NEQ; }
{single_op} { return yytext[0]; }
    /* id */
{identifier} {
    yylval->sv_str = yytext;
    return IDENTIFIER;
}
    /* literals */
{value_int} {
    try {
        long long int val = std::stoll(yytext);
        if (val < std::numeric_limits<int>::min() || val > std::numeric_limits<int>::max()) {
            //若超出int范围 则为long long
            yylval->sv_bigInt = val;
            return VALUE_BIGINT;
        } 
        yylval->sv_int = atoi(yytext);
        return VALUE_INT;
    } catch (const std::out_of_range& e) {
        throw InternalError("failure");
    }
}
{value_float} {
    yylval->sv_float = atof(yytext);
    return VALUE_FLOAT;
}
{value_datetime} { 
    std::string datetime = std::string(yytext + 1, strlen(yytext) - 2);
    if (check_time(datetime)) {
        yylval->sv_time = datetime;
        return VALUE_DATETIME;
    } else {
        throw InternalError("failure");
    }    
}
{value_string} {
    yylval->sv_str = std::string(yytext + 1, strlen(yytext) - 2);
    return VALUE_STRING;
}
    /* EOF */
<<EOF>> { return T_EOF; }
    /* unexpected char */
. { std::cerr << "Lexer Error: unexpected character " << yytext[0] << std::endl; }
%%

bool check_time(std::string &datetime)
{
    //格式检查
    if(datetime.size()!=19)
        return false;

    if(datetime<"1000-01-01 00:00:00"||datetime>"9999-12-31 23:59:59")
        return false;

    int year=std::stoi(datetime.substr(0,4));
    int month=std::stoi(datetime.substr(5,2));
    int day=std::stoi(datetime.substr(8,2));
    int hour=std::stoi(datetime.substr(11,2));
    int minute=std::stoi(datetime.substr(14,2));
    int second=std::stoi(datetime.substr(17,2));

    //时间检查
    if(hour>=24||minute>59||second>59)
        return false;

    //日期检查
    if (month==4 || month==6 || month==9 || month==11) {
        return day<=30;
    }
    else if (month == 2) {
        if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
            return day <= 29&&day>0;
        else
            return day <= 28&&day>0;
    }
    else if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)
        return day <= 31&&day>0;
    else
        return false;
}